package com.liuyu.consumer.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.retry.MessageRecoverer;
import org.springframework.amqp.rabbit.retry.RepublishMessageRecoverer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 该类是声明消息被消费失败的一种消息处理方法
 *  总的流程
 *  消费者失败确认机制开启：
 *      1.在配置文件中添加acknowledge-mode：auto  设置由spring自动返回消费处理成功的ack或者失败的nack
 *      2.存在弊端在消息消费失败时，会频繁的将消息入队并且重新消费，如果一直消费不成功则浪费资源
 *      3.解决方案：在spring本地进行失败重试，不再来回入队
 *      4.做法：在配置文件中添加如下配置
 *      retry:
 *           enabled: true   #开启消费者失败重试
 *           initial-interval: 1000ms #初始失败等待时间为1秒
 *           multiplier: 2 #失败等待时常背书，下次等待时常
 *           max-attempts: 3 #最大重试次数
 *           stateless: true #true无状态，false有状态，如果业务中包含事务这里改为false
 *       5.本地重试结束后依然失败消息处理策略：
 *         # RejectAndDontRequeueRecoverer 丢弃消息（默认）
 *         # ImmediateRequeueMessageRecoverer 重试结束后重新入队重新入队
 *         # RepublishMessageRecoverer 重试失败后将消息投递到指定的交换机（由人工做特殊处理）
 *       6.因此有了该类用来声明本地重试失败后处理消息的策略：本来=类采用的是RepublishMessageRecoverer处理模式：
 *          在本类中声明了失败消息投递的交换机和失败消息的队列并且声明了消息处理重试失败的消息的策略
 *
 *
 *
 */
@Configuration
@Slf4j
public class ErrorMsgConfig {
    @Bean
    public DirectExchange errorMsgExchange() {
        return new DirectExchange("error.msg.exchange", true, false);
    }

    @Bean
    public Queue errorMsgQueue() {
        return new Queue("error.msg.queue", true);
    }

    @Bean
    public Binding errorMsgBinding(Queue errorMsgQueue, DirectExchange errorMsgExchange) {
        return BindingBuilder.bind(errorMsgQueue).to(errorMsgExchange).with("error.msg");
    }

    /**
     * 声明本地重试结束依然消费失败的消息的策略，即将本地重试依然消费失败的消息投递到本类生命的交换机和消息队列中，监听该失败队列中的消息即可获取到失败的消息
     *
     * @param rabbitTemplate 注入rabbitTemplate 没有显示注入因为 方法上有注解@Bean则参数上等于有@Autowired注解
     */
    @Bean
    public MessageRecoverer republishMessageRecoverer(RabbitTemplate rabbitTemplate) {
        return new RepublishMessageRecoverer(rabbitTemplate, "error.msg.exchange", "error.msg");
    }
}
